package com.coderman.infosys.auth.app.command;


import com.coderman.infosys.auth.api.dto.authority.request.CreateAuthorityRequestDTO;
import com.coderman.infosys.auth.api.dto.authority.request.UpdateAuthorityRequestDTO;
import com.coderman.infosys.auth.domain.bo.authority.AdminAuthorityBO;
import com.coderman.infosys.auth.domain.bo.authority.AuthorityBO;
import com.coderman.infosys.auth.domain.bo.authority.SystemAuthorityBO;
import com.coderman.infosys.plat.auth.common.enums.StatusEnum;
import com.google.common.collect.Lists;
import lombok.Builder;
import lombok.Data;
import lombok.ToString;
import org.apache.commons.collections.CollectionUtils;

import java.util.*;

/**
* @Description:对角色权限类
* @Author:shenshuai
* @CreateTime:2022-01-02 15:27:38
* @version v1.0
*/
@Data
@ToString
@Builder
public class AuthToRoleCmd extends  AbstractCmd{


   /** 角色ID **/
   private String  roleCode;

   /** 权限编码标示 **/
   private String authCode;

   /** 权限类型 **/
   @Deprecated
   private Integer authorityType;


   /** 权限类型 这个权限类型应该是有多个，查询的时候可以具体通过AuthorityTypeEnum来路由查询 **/
   /** 或者查询的时候底层通过联表查询，然后构建整个权限聚合根 **/
   private Set<String> authorityTypeSet;

   /** 权限状态 **/
   private Integer status;

   /**
    * 系统-菜单-按钮权限列表
    */
   private Map<String, Map<String, List<String>>> systemCodeMap;

   /**
    * 应用-表-字段按钮权限列表
    */
   private Map<String, Map<String, List<String>>> dataColumnMap;


   /**
    * 部门权限
    */
   private List<Long> departIdList;
   /**
    * 省-城市-县权限列表
    */
   private Map<String, Map<String, List<String>>> addressMap;
   /**
    * 职位权限
    */
   private List<Long> jobIdList;
   /**
    *
    * 上面这些传入的参数都是比较基本的类型，那么现在有个问题，需要调用聚合根接口来为角色构建权限
    * 因此出现了映射偏移，那下面的这些对象是从AuthorityBO中获得的
    * private List<DataAuthorityBO>  dataAuthorityBOList；
    * private List<SystemAuthorityBO>  systemAuthorityBOList;
    *  private List<AdminAuthorityBO>  adminAuthorityBOList;
    *
    *  这里其实有一些讲究
    *  1.接口传输对于这种关联型的对象可能会比较简单,因此是一种基本类型，那在领域层如果是简单类型对于读场景来说就比较符合dto->bo
    *  的转换，因为这样简单方便，因此就出现了一个可能的问题就是设计领域模型的时候可能会因为接口参数或者接口设计的问题导致妥协
    *  那对于领域模型而言读和写都会间接受到影响，因此不管是使用convert接口也好，factory工厂或者builder构建其实都可能已经
    *  让领域模型变得不再纯粹。领域层BO本身会被数据在底层和接口层的表示形式影响，所以复杂场景下建议进行关注点分离，让读写和各自层次的模型保持各自的边界
    *
    *  2.那对于cmd层的参数是取自接口层DTO的还是BO层的其实可以从几个方面来考虑，如果cmd在业务执行之后需要依赖BO层面的参数做一些业务那么这里可以将BO里面的参数当作cmd里面的参数
    *  如果不需要的化可以将DTO里面的参数当作cmd里面的参数，由于模型之间有偏差对于cmd本身而言其参数是对DTO的引用还是DTO的参数子集或者BO的子集合都不重要，重要的是cmd能良好的让两边
    *  都保持各自的空间
    *
    *  3.那么问题出现了，由于接口参数比较简单，但是领域参数都是对象的，因此为了解决这种映射偏移的情况，
    *  可以在app层构建基于dto->bo的Factory工厂类,或者通过Convert接口转换(这种方式无法解决更复杂的映射)
    *  或者在app层的facadeImpl实现类里面增加一个转换方法，当然这些方式都是借助外部的类来完成这种映射，所以这些外部类可能就变得有点腐化的意思，同时这种更复杂的转换
    *  也深深的嵌入到了方法的主流程中了，因此会在迭代中变得更加难以维护，那现在的这个方法就是通过cmd这一层来帮忙消化一下这块工作量我们来看一下效果，
    /**

   /**
    * 在cmd里面衔接dto和bo
    * 该cmd充当工厂和对象转换的角色
    *
    * 这里我们先让cmd兼容DTO层面的要求，后面再由cmd本身去转换bo对象，因此cmd+exe就可以在facadeimpl-domain之间形成一个微小的应用层空间
    * @param createAuthorityRequestDTO
    * @return
    */
   public  static AuthToRoleCmd buildCmd(CreateAuthorityRequestDTO createAuthorityRequestDTO){

      AuthToRoleCmd authToRoleCmd = AuthToRoleCmd.builder()
              .addressMap(createAuthorityRequestDTO.getAddressMap())
              .authCode(createAuthorityRequestDTO.getAuthCode())
              .authorityTypeSet(createAuthorityRequestDTO.getAuthorityTypeSet())
              .dataColumnMap(createAuthorityRequestDTO.getDataColumnMap())
              .roleCode(createAuthorityRequestDTO.getRoleCode())
              .departIdList(createAuthorityRequestDTO.getDepartIdList())
              .status(createAuthorityRequestDTO.getStatus())
              .jobIdList(createAuthorityRequestDTO.getJobIdList())
              .build();
      authToRoleCmd.setOperateUserId(createAuthorityRequestDTO.getOperateUserId());


      if(createAuthorityRequestDTO.getStatus() == null){
         //设置初始化状态
         authToRoleCmd.setStatus(StatusEnum.INIT.getStatus());
      }
      return authToRoleCmd;
   }


   public  static AuthToRoleCmd buildCmd(UpdateAuthorityRequestDTO createAuthorityRequestDTO){

      AuthToRoleCmd addUserGroupCmd = AuthToRoleCmd.builder()
              .addressMap(createAuthorityRequestDTO.getAddressMap())
              .authCode(createAuthorityRequestDTO.getAuthCode())
              .authorityTypeSet(createAuthorityRequestDTO.getAuthorityTypeSet())
              .dataColumnMap(createAuthorityRequestDTO.getDataColumnMap())
              .roleCode(createAuthorityRequestDTO.getRoleCode())
              .departIdList(createAuthorityRequestDTO.getDepartIdList())
              .status(createAuthorityRequestDTO.getStatus())
              .jobIdList(createAuthorityRequestDTO.getJobIdList())
              .build();
      addUserGroupCmd.setOperateUserId(createAuthorityRequestDTO.getOperateUserId());

      //通常情况下数据库默认值即可，但是这里可能有点钻牛角尖了,
      if(createAuthorityRequestDTO.getStatus() == null){
         //设置初始化状态
         addUserGroupCmd.setStatus(StatusEnum.INIT.getStatus());
      }
      return addUserGroupCmd;
   }




   public AuthorityBO buildAuthorityBO(){
      AuthorityBO authorityBO = new AdminAuthorityBO();
      authorityBO.setAuthorityTypeSet(this.getAuthorityTypeSet());
      authorityBO.setRoleCode(roleCode);
      authorityBO.setAuthCode(authCode);
      authorityBO.setAdminAuthorityBOList(convert2DataAuthorityList());
      authorityBO.setSystemAuthorityBOList(convert2SystemAuthorityList());
      // todo : authorityBO.setDataAuthorityBOList();

      return authorityBO;
   }


   private List<SystemAuthorityBO> convert2SystemAuthorityList(){
      List<SystemAuthorityBO> systemAuthorityBOList = new ArrayList<>();
      if(this.systemCodeMap.isEmpty()){
         return systemAuthorityBOList;
      }
      this.systemCodeMap.forEach((systemCode,moduleMap)->{
         SystemAuthorityBO systemAuthorityBO = SystemAuthorityBO.ofIdentifer(this.authCode);
         systemAuthorityBO.setSystemCode(systemCode);
         moduleMap.forEach((moduleCode,menuList)->{
            menuList.forEach(menuCode->{
               systemAuthorityBOList.add(systemAuthorityBO.build(systemCode,moduleCode,menuCode));
            });
         });
      });
      return systemAuthorityBOList;
   }



   private List<AdminAuthorityBO> convert2DataAuthorityList(){

      List<AdminAuthorityBO> list = new ArrayList<>();
      int maxLength = getMaxLength();

      List<Map<String,List<String>>> listMap = (List<Map<String,List<String>>>)addressMap.values();
      Set<String> set = new HashSet<>();
      listMap.forEach(map->{
         set.addAll(map.keySet());
      });

      List<String> cityIdList = Lists.newArrayList(set);

      for (int i = 0;i < maxLength;i++ ){
         AdminAuthorityBO authorityBO = AdminAuthorityBO.ofIdentifer(authCode);
         if(i < this.departIdList.size()){
            authorityBO.setDepartmentId(this.getDepartIdList().get(i));
         }
         if(i < this.jobIdList.size()){
            authorityBO.setJobId(this.getJobIdList().get(i));
         }
         if(i < cityIdList.size()){
            authorityBO.setCityId(Long.parseLong(cityIdList.get(i)));
         }
         list.add(authorityBO);
      }
      return list;

   }

   private int getMaxLength(){
      int maxLength = 0;
      if(CollectionUtils.isNotEmpty(this.departIdList)){
         maxLength = this.departIdList.size();
      }
      if(CollectionUtils.isNotEmpty(this.jobIdList) && this.jobIdList.size() > maxLength){
         maxLength = this.departIdList.size();
      }

      if(this.addressMap != null&& this.addressMap.size() > maxLength){
         maxLength = this.addressMap.size();
      }
      return maxLength;
   }

}